/*
 * @Author: zhangshu
 * @email: shiliang.zhang@tuya.com
 * @LastEditors: Please set LastEditors
 * @file name: app_gpio.c
 * @Description: 
 * @Copyright: HANGZHOU TUYA INFORMATION TECHNOLOGY CO.,LTD
 * @Company: http://www.tuya.com
 * @Date: 2022-02-08
 * @LastEditTime: 2022-02-08
 */
#include "tal_memory.h"
#include "tal_gpio.h"
#include "tal_sw_timer.h"

#include "app_common.h"
#include "app_gpio.h"
#include "app_mfg.h"

#define VIBRATE_TIME                      200

//sensor gpio state enum, active when trigger and inactive when normal
typedef enum {
    GPIO_SENSOR_INACTIVE = 0x00,
    GPIO_SENSOR_ACTIVE   = 0x01,
} GPIO_STATE_E;

//gpio interrupt status, include new status and last status
typedef struct {
    GPIO_STATE_E new_status;
    GPIO_STATE_E last_status;
} INT_STATUS_T;

//all sensor interrupt status, 
typedef struct {
    INT_STATUS_T alarm_int;
    INT_STATUS_T tamper_int;
    INT_STATUS_T trouble_int;
} SENSOR_INT_STATUS_T;

//sensor int status
STATIC SENSOR_INT_STATUS_T sensor_status;
//sensor interrupt infomation
STATIC APP_GPIO_INFO_T *int_info;

//all sensor interrupt event and handler
TIMER_ID etimer_alarm1_debounce_event_id;
TIMER_ID etimer_tamper_debounce_event_id;
TIMER_ID etimer_trouble_debounce_event_id;
STATIC VOID_T gpio_alarm1_debounce_handler(TIMER_ID timer_id, VOID_T *arg);
STATIC VOID_T gpio_tamper_debounce_handler(TIMER_ID timer_id, VOID_T *arg);
STATIC VOID_T gpio_trouble_debounce_handler(TIMER_ID timer_id, VOID_T *arg);
//app gpio event create
STATIC VOID_T app_gpio_event_init(VOID_T);
//gpio interrupt callback handler, register when config irq, please referance TUYA_GPIO_IRQ_CB
STATIC VOID_T gpio_sensor_alarm1_interrupt_handler(VOID_T *args);
STATIC VOID_T gpio_sensor_tamper_interrupt_handler(VOID_T *args);
STATIC VOID_T gpio_sensor_trouble_interrupt_handler(VOID_T *args);
//gpio interrupt debounce
STATIC VOID_T gpio_sensor_alarm1_debounce(GPIO_STATE_E new_status);
STATIC VOID_T gpio_sensor_tamper_debounce(GPIO_STATE_E new_status);
STATIC VOID_T gpio_sensor_trouble_debounce(GPIO_STATE_E new_status);
//gpio status changed after interrupt debounce
STATIC VOID_T ias_zone_alarm1_gpio_status_change_callback(GPIO_STATE_E gp_status);
STATIC VOID_T ias_zone_tamper_gpio_status_change_callback(GPIO_STATE_E gp_status);
STATIC VOID_T ias_zone_trouble_gpio_status_change_callback(GPIO_STATE_E gp_status);

/**
 * @description: app gpio interrupt init, include alarm tamper and trouble
 * @param[in] gpio_info: gpio info, please refer to APP_GPIO_INFO_T
 * @return: OPERATE_RET, success when OPRT_OK, or else
 */
OPERATE_RET app_gpio_oem_init(APP_GPIO_INFO_T *gpio_info)
{
    STATIC UINT8_T is_init = 0;

    if (is_init || gpio_info == NULL) {
        TAL_PR_TRACE("gpio already init or gpio info null\r\n");
        return OPRT_COM_ERROR;
    }

    int_info = (APP_GPIO_INFO_T *)tal_malloc(SIZEOF(APP_GPIO_INFO_T));

    if (int_info == NULL) {
        TAL_PR_TRACE("[malloc error] app gpio malloc error\r\n");
        return OPRT_MALLOC_FAILED;
    }

    is_init = 1;
    tal_system_memset(int_info, 0, SIZEOF(APP_GPIO_INFO_T));
    tal_system_memcpy(int_info, gpio_info, SIZEOF(APP_GPIO_INFO_T));

    TUYA_GPIO_IRQ_T gpio_irq;
    gpio_irq.arg = NULL;
    gpio_irq.mode = TUYA_GPIO_IRQ_RISE_FALL;
    if (int_info->alarm.enabled) {
        gpio_irq.cb = gpio_sensor_alarm1_interrupt_handler;
        tal_gpio_irq_init(int_info->alarm.pin_num, &gpio_irq);
    }
    if (int_info->tamper.enabled) {
        gpio_irq.cb = gpio_sensor_tamper_interrupt_handler;
        tal_gpio_irq_init(int_info->tamper.pin_num, &gpio_irq);
    }
    if (int_info->trouble.enabled) {
        gpio_irq.cb = gpio_sensor_trouble_interrupt_handler;
        tal_gpio_irq_init(int_info->trouble.pin_num, &gpio_irq);
    }
    TAL_PR_TRACE("int init OK!!!");
    return OPRT_OK;
}

/**
 * @description: gpio init after preheat time delay, gpio state may not stable during the preheat time
 * @param[in] none
 * @return: none
 */
VOID_T app_gpio_preheat_init(VOID_T)
{
    STATIC UINT8_T first_flag = 0;
    if (!first_flag) {
        first_flag = 1;
        tal_system_memset(&sensor_status, 0, SIZEOF(SENSOR_INT_STATUS_T));

        TUYA_GPIO_LEVEL_E alarm_state = TUYA_GPIO_LEVEL_LOW;
        TUYA_GPIO_LEVEL_E tamper_state = TUYA_GPIO_LEVEL_LOW;
        TUYA_GPIO_LEVEL_E trouble_state = TUYA_GPIO_LEVEL_LOW;

        tal_gpio_read(int_info->alarm.pin_num, &alarm_state);
        sensor_status.alarm_int.last_status = (GPIO_STATE_E)alarm_state;
        sensor_status.alarm_int.new_status = sensor_status.alarm_int.last_status;

        tal_gpio_read(int_info->tamper.pin_num, &tamper_state);
        sensor_status.tamper_int.last_status = (GPIO_STATE_E)tamper_state;
        sensor_status.tamper_int.new_status = sensor_status.tamper_int.last_status;

        tal_gpio_read(int_info->trouble.pin_num, &trouble_state);
        sensor_status.trouble_int.last_status = (GPIO_STATE_E)trouble_state;
        sensor_status.trouble_int.new_status = sensor_status.trouble_int.last_status;

        ias_zone_alarm1_gpio_status_change_callback(sensor_status.alarm_int.new_status);
        ias_zone_tamper_gpio_status_change_callback(sensor_status.tamper_int.new_status);
        ias_zone_trouble_gpio_status_change_callback(sensor_status.trouble_int.new_status);

        app_gpio_event_init();
    }
}

/**
 * @description: gpio event create
 * @param[in] none
 * @return: none
 */
STATIC VOID_T app_gpio_event_init(VOID_T)
{
    tal_sw_timer_create(gpio_alarm1_debounce_handler, NULL, &etimer_alarm1_debounce_event_id);
    tal_sw_timer_create(gpio_tamper_debounce_handler, NULL, &etimer_tamper_debounce_event_id);
    tal_sw_timer_create(gpio_trouble_debounce_handler, NULL, &etimer_trouble_debounce_event_id);
}

/**
 * @description: irq callback, alarm interrupt handler
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_sensor_alarm1_interrupt_handler(VOID_T *args)
{
    GPIO_STATE_E state = GPIO_SENSOR_INACTIVE;
    TUYA_GPIO_LEVEL_E level = TUYA_GPIO_LEVEL_LOW;

    if (!int_info->alarm.enabled) {
        return;
    }

    tal_gpio_read(int_info->alarm.pin_num, &level);

    if (int_info->alarm.driver_flag == level) {
        state = GPIO_SENSOR_ACTIVE;
    } else {
        state = GPIO_SENSOR_INACTIVE;
    }
    TAL_PR_TRACE("alarm1 interrupt, level %d, state %d\r\n", level, state);
    gpio_sensor_alarm1_debounce(state);
}

/**
 * @description: irq callback, tamper interrupt handler
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_sensor_tamper_interrupt_handler(VOID_T *args)
{
    GPIO_STATE_E state = GPIO_SENSOR_INACTIVE;
    TUYA_GPIO_LEVEL_E level = TUYA_GPIO_LEVEL_LOW;

    if (!int_info->tamper.enabled) {
        return;
    }

    tal_gpio_read(int_info->tamper.pin_num, &level);


    if (int_info->tamper.driver_flag == level) {
        state = GPIO_SENSOR_ACTIVE;
    } else {
        state = GPIO_SENSOR_INACTIVE;
    }
    TAL_PR_TRACE("tamper interrupt, level %d, state %d\r\n", level, state);
    gpio_sensor_tamper_debounce(state);
}

/**
 * @description: irq callback, trouble interrupt handler
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_sensor_trouble_interrupt_handler(VOID_T *args)
{
    GPIO_STATE_E state = GPIO_SENSOR_INACTIVE;
    TUYA_GPIO_LEVEL_E level = TUYA_GPIO_LEVEL_LOW;

    if (!int_info->trouble.enabled) {
        return;
    }

    tal_gpio_read(int_info->trouble.pin_num, &level);


    if (int_info->trouble.driver_flag == level) {
        state = GPIO_SENSOR_ACTIVE;
    } else {
        state = GPIO_SENSOR_INACTIVE;
    }
    TAL_PR_TRACE("trouble interrupt, level %d, state %d\r\n", level, state);
    gpio_sensor_trouble_debounce(state);
}

/**
 * @description: gpio alarm1 debounce
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_alarm1_debounce_handler(TIMER_ID timer_id, VOID_T *arg)
{
    sensor_status.alarm_int.last_status = sensor_status.alarm_int.new_status;
    ias_zone_alarm1_gpio_status_change_callback(sensor_status.alarm_int.new_status);
}

/**
 * @description: gpio tamper debounce
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_tamper_debounce_handler(TIMER_ID timer_id, VOID_T *arg)
{
    sensor_status.tamper_int.last_status = sensor_status.tamper_int.new_status;
    ias_zone_tamper_gpio_status_change_callback(sensor_status.tamper_int.new_status);
}

/**
 * @description: gpio trouble debounce
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_trouble_debounce_handler(TIMER_ID timer_id, VOID_T *arg)
{
    sensor_status.trouble_int.last_status = sensor_status.trouble_int.new_status;
    ias_zone_trouble_gpio_status_change_callback(sensor_status.trouble_int.new_status);
}

/**
 * @description: ias zone alarm1 gpio debounce func
 * @param [in] new_status: gpio state active or inactive
 * @return: none
 */
STATIC VOID_T gpio_sensor_alarm1_debounce(GPIO_STATE_E new_status)
{
    if (new_status == (GPIO_STATE_E)sensor_status.alarm_int.last_status) {
        tal_sw_timer_stop(etimer_alarm1_debounce_event_id);
        return;
    }
    sensor_status.alarm_int.new_status = new_status;
    tal_sw_timer_start(etimer_alarm1_debounce_event_id, VIBRATE_TIME, TAL_TIMER_ONCE);
}

/**
 * @description: ias zone tamper gpio debounce func
 * @param [in] new_status: gpio state active or inactive
 * @return: none
 */
STATIC VOID_T gpio_sensor_tamper_debounce(GPIO_STATE_E new_status)
{
    if (new_status == (GPIO_STATE_E)sensor_status.tamper_int.last_status) {
        tal_sw_timer_stop(etimer_tamper_debounce_event_id);
        return;
    }
    sensor_status.tamper_int.new_status = new_status;
    tal_sw_timer_start(etimer_tamper_debounce_event_id, VIBRATE_TIME, TAL_TIMER_ONCE);
}

/**
 * @description: ias zone trouble gpio debounce func
 * @param [in] new_status: gpio state active or inactive
 * @return: none
 */
STATIC VOID_T gpio_sensor_trouble_debounce(GPIO_STATE_E new_status)
{
    if (new_status == (GPIO_STATE_E)sensor_status.trouble_int.last_status) {
        tal_sw_timer_stop(etimer_trouble_debounce_event_id);
        return;
    }
    sensor_status.trouble_int.new_status = new_status;
    tal_sw_timer_start(etimer_trouble_debounce_event_id, VIBRATE_TIME, TAL_TIMER_ONCE);
}

/**
 * @description: ias zone alarm1 gpio status change callback
 * @param [in] gp_status: gpio state active or inactive
 * @return: none
 */
STATIC VOID_T ias_zone_alarm1_gpio_status_change_callback(GPIO_STATE_E gp_status)
{
    TAL_PR_TRACE("mfg_alarm1_test_callback [%d] \r\n", gp_status);
    if (mfg_alarm1_test_callback(gp_status)) {
        return;
    }
    if(gp_status == GPIO_SENSOR_INACTIVE) {
        alarm_switch_alarm1_inactive();
    }
    else {
        alarm_switch_alarm1_active();
    }
}

/**
 * @description: ias zone tamper gpio status change callback
 * @param [in] gp_status: gpio state active or inactive
 * @return: none
 */
STATIC VOID_T ias_zone_tamper_gpio_status_change_callback(GPIO_STATE_E gp_status)
{
    if (mfg_tamper_test_callback(gp_status)) {
        return;
    }
    if(gp_status == GPIO_SENSOR_INACTIVE) {
        tamper_switch_tamper_inactive();
    }
    else {
        tamper_switch_tamper_active();
    }
}

/**
 * @description: ias zone trouble gpio status change callback
 * @param [in] gp_status: gpio state active or inactive
 * @return: none
 */
STATIC VOID_T ias_zone_trouble_gpio_status_change_callback(GPIO_STATE_E gp_status)
{
    if (mfg_trouble_test_callback(gp_status)) {
        return;
    }
    if(gp_status == GPIO_SENSOR_INACTIVE) {
        trouble_switch_trouble_inactive();
    }
    else {
        trouble_switch_trouble_active();
    }
}

